﻿/*
 * Copyright (c) 2011
 * Thomas Rozanski, Geoffrey Wright, Zachary Lynn, Jesse Natalie
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the team nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THOMAS ROZANSKI, GEOFFREY WRIGHT,
 * ZACHARY LYNN, OR JESSE NATALIE BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading.Tasks;

namespace Kinected
{
	/// <summary>The fake Kinect camera.</summary>
	/// <seealso cref="Camera"/>
	/// <seealso cref="RealCamera"/>
	public class MockCamera : Camera
	{
		private RGBAImage rgbaColorImage, rgbaDepthImage;
		private RAWImage rawDepthImage;

        private MockupWindow drawingWindow;

		public MockCamera()
		{
			rgbaColorImage = new RGBAImage(640, 480);
			rgbaDepthImage = new RGBAImage(640, 480);
			rawDepthImage = new RAWImage(640, 480);
            Start();
		}

		~MockCamera()
		{

		}
		
		/// <summary>Starts the camera and shows the MockupWindow.</summary>
		public override bool Start()
		{
            drawingWindow = new MockupWindow();
            drawingWindow.Show();
			return true;
		}

		/// <summary>Stops the camera.</summary>
		public override bool Stop()
		{
            if (drawingWindow != null)
            {
                drawingWindow.Close();
                drawingWindow = null;
            }
			return true;
		}

        /// <summary>Converts drawingWindow's WindowBitmap to useable RGBAImages.</summary>
        private void GenerateColorImages()
        {
            Rectangle bmpRect = new Rectangle(0, 0, drawingWindow.WindowBitmap.Width, drawingWindow.WindowBitmap.Height);

            // lock the bits for our use
            System.Drawing.Imaging.BitmapData bmpData = drawingWindow.WindowBitmap.LockBits(bmpRect,
                System.Drawing.Imaging.ImageLockMode.ReadOnly, drawingWindow.WindowBitmap.PixelFormat);

            // first line of the bitmap
            IntPtr bmpDataPtr = bmpData.Scan0;

            int numBytes = rgbaColorImage.Width * rgbaColorImage.Stride * rgbaColorImage.Height;

            // copy the bitmap to RGBAImage's array
            System.Runtime.InteropServices.Marshal.Copy(bmpDataPtr, rgbaColorImage.Data, 0, numBytes);
            System.Runtime.InteropServices.Marshal.Copy(bmpDataPtr, rgbaDepthImage.Data, 0, numBytes);

            // Unlock the bits
            drawingWindow.WindowBitmap.UnlockBits(bmpData);
        }

        /// <summary>Converts rgbaDepthImage to a RAWImage.</summary>
        private void GenerateRAWDepthImage()
        {
            int rgbaRowSize = rgbaDepthImage.Width * rgbaDepthImage.Stride;
            int rawRowSize = rawDepthImage.Width * rawDepthImage.Stride;

            Parallel.For(0, rgbaDepthImage.Height, y =>
            {
                int currentRGBARow = y * rgbaRowSize;
                int currentRAWRow = y * rawRowSize;
                int z = 0;
                short rawValue;

                for (int x = 0; x < rgbaRowSize; x += rgbaDepthImage.Stride)
                {
                    rawValue = 1000;
                    if (rgbaDepthImage.Data[currentRGBARow + x + 1] == 0)
                        rawValue = 950;

                    rawDepthImage.Data[currentRAWRow + z] = (byte)(rawValue);
                    rawDepthImage.Data[currentRAWRow + z + 1] = (byte)(rawValue >> 8);

                    z += rawDepthImage.Stride;
                }
            });
        }

        /// <summary>Gets the color image.</summary>
        /// <returns>Image with RGBA data.</returns>
		public override RGBAImage GetRGBAColorImage()
        {
            if (drawingWindow.WindowHasChanged)
            {
                GenerateColorImages();
                GenerateRAWDepthImage();
                drawingWindow.WindowHasChanged = false;
            }
            return rgbaColorImage;
        }

        /// <summary>Gets the depth image.</summary>
        /// <returns>Depth image with RGBA data.</returns>
		public override RGBAImage GetRGBADepthImage()
        {
            if (drawingWindow.WindowHasChanged)
            {
                GenerateColorImages();
                GenerateRAWDepthImage();
                drawingWindow.WindowHasChanged = false;
            }
            return rgbaDepthImage;
        }

        /// <summary>Gets the depth image.</summary>
        /// <returns>Depth image with RAW data.</returns>
		public override RAWImage GetRAWDepthImage()
        {
            if (drawingWindow.WindowHasChanged)
            {
                GenerateColorImages();
                GenerateRAWDepthImage();
                drawingWindow.WindowHasChanged = false;
            }
            return rawDepthImage;
        }
	}
}